home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1995 November / EnigmA AMIGA RUN 02 (1995)(G.R. Edizioni)(IT)[!][issue 1995-11][Skylink CD].iso / earcd / program / misc / bgui12.lha / Classes / PopButtonClass / popbuttonclass.c next >
C/C++ Source or Header  |  1995-08-18  |  34KB  |  1,540 lines

  1. /*
  2. **           File: popbuttonclass.c
  3. **    Description: BOOPSI (BGUI) popup-menu button class.
  4. **      Copyright: (C) Copyright 1995 Jaba Development.
  5. **             (C) Copyright 1995 Jan van den Baard.
  6. **             All Rights Reserved.
  7. **/
  8.  
  9. #include <exec/types.h>
  10. #include <exec/libraries.h>
  11. #include <exec/memory.h>
  12. #include <intuition/intuition.h>
  13. #include <intuition/icclass.h>
  14. #include <graphics/gfxmacros.h>
  15. #include <libraries/bgui.h>
  16.  
  17. #include <clib/alib_protos.h>
  18.  
  19. #include <proto/exec.h>
  20. #include <proto/intuition.h>
  21. #include <proto/graphics.h>
  22. #include <proto/bgui.h>
  23. #include <proto/utility.h>
  24.  
  25. #include <string.h>
  26.  
  27. #include "popbuttonclass.h"
  28.  
  29. /*
  30. **    Compiler stuff.
  31. **/
  32. #ifdef _DCC
  33. #define SAVEDS __geta4
  34. #define ASM
  35. #define REG(x) __ ## x
  36. #else
  37. #define SAVEDS __saveds
  38. #define ASM __asm
  39. #define REG(x) register __ ## x
  40. #endif
  41.  
  42. /*
  43. **    Type-cast.
  44. **/
  45. #define GADGET(x)    (( struct Gadget * )x )
  46.  
  47. /*
  48. **    OS macros.
  49. **/
  50. #define OS30        (( struct Library * )SysBase)->lib_Version >= 39
  51. #define OS20        (( struct Library * )SysBase)->lib_Version < 39
  52.  
  53. /*
  54. **    PopupButtonClass object instance data.
  55. **/
  56. typedef struct {
  57.     struct PopMenu           *pmd_MenuLabels;         /* Menu labels.                 */
  58.     ULONG            pmd_NumMenuLabels;    /* Number of menu labels.    */
  59.     ULONG            pmd_PopPos;        /* Menu under the mouse.    */
  60.     struct Image           *pmd_Image;        /* Image for the button.    */
  61.     Object               *pmd_VectorImage;    /* Vector image for the button. */
  62.     Object               *pmd_CheckMark;        /* CheckMark image.        */
  63.     UWORD            pmd_CheckWidth;         /* Scaled checkmark width.    */
  64.     UWORD            pmd_CheckHeight;    /* Scaled checkmark height.    */
  65.     struct Window           *pmd_PopWindow;        /* Popup window pointer.    */
  66.     UWORD            pmd_PopWindowWidth;    /* Width of the pop window.    */
  67.     UWORD            pmd_PopWindowHeight;    /* Height of the pop window.    */
  68.     struct TextFont        *pmd_PopFont;        /* Pop window font.        */
  69.     ULONG            pmd_Selected;        /* Selected menu number.    */
  70.     UWORD            pmd_Flags;        /* See below.            */
  71. } PMD;
  72.  
  73. #define PMDF_NOEVENT        (1<<0)            /* ActivateGadget() activation. */
  74.  
  75. /*
  76. **    I need a new checkmark image which is "tighter"
  77. **    than the built-in BGUI one.
  78. **/
  79. STATIC const struct VectorItem myCheckMark[] = {
  80.     {   14,     8,        VIF_SCALE            },
  81.     {   1,        4,        VIF_MOVE | VIF_AREASTART    },
  82.     {   4,        7,        VIF_DRAW            },
  83.     {   6,        7,        VIF_DRAW            },
  84.     {   12,     1,        VIF_DRAW            },
  85.     {   13,     1,        VIF_DRAW            },
  86.     {   11,     1,        VIF_DRAW            },
  87.     {   6,        6,        VIF_DRAW            },
  88.     {   5,        6,        VIF_DRAW            },
  89.     {   3,        4,        VIF_DRAW            },
  90.     {   1,        4,        VIF_LASTITEM        },
  91. };
  92.  
  93. /*
  94. **    Default drawinfo pens. Just in
  95. **    case we don't get 'm from the
  96. **    system.
  97. **/
  98. STATIC UWORD DefDriPens[ 12 ] = { 0, 1, 1, 2, 1, 3, 1, 0, 2, 1, 2, 1 };
  99.  
  100. /*
  101. **    Version checking.
  102. **/
  103. extern struct Library *SysBase;
  104.  
  105. /*
  106. **    For filtering vectorclass attributes.
  107. **    Ofcourse this array needs to be updated
  108. **    when there are attributes added to the
  109. **    vectorclass.
  110. **/
  111. STATIC ULONG VectorAttrs[] = {    VIT_VectorArray,
  112.                 VIT_BuiltIn,
  113.                 VIT_Pen,
  114.                 VIT_DriPen,
  115.                 TAG_END };
  116.  
  117. /*
  118. **    See if we should create a vector image.
  119. **/
  120. STATIC ASM Object *CreateVectorImage( REG(a1) struct TagItem *attrs )
  121. {
  122.     Class            *class;
  123.     struct TagItem        *clones;
  124.     Object            *vector = NULL;
  125.  
  126.     /*
  127.     **    Get a pointer to the vectorclass.
  128.     **/
  129.     if ( class = BGUI_GetClassPtr( BGUI_VECTOR_IMAGE )) {
  130.         /*
  131.         **    We clone the original taglist
  132.         **    to filter out any unwanted
  133.         **    attributes.
  134.         **/
  135.         if ( clones = CloneTagItems( attrs )) {
  136.             /*
  137.             **    Filter out any non-vectorclass
  138.             **    attributes.
  139.             **/
  140.             if ( FilterTagItems( clones, VectorAttrs, TAGFILTER_AND ))
  141.                 /*
  142.                 **    Create the vector image.
  143.                 **/
  144.                 vector = NewObjectA( class, NULL, clones );
  145.             /*
  146.             **    Free the clones.
  147.             **/
  148.             FreeTagItems( clones );
  149.         }
  150.     }
  151.     return( vector );
  152. }
  153.  
  154. /*
  155. **    Notify about an attribute change.
  156. **/
  157. STATIC ULONG NotifyAttrChange( Object *obj, struct GadgetInfo *gi, ULONG flags, Tag tag1, ... )
  158. {
  159.     return( DoMethod( obj, OM_NOTIFY, &tag1, gi, flags ));
  160. }
  161.  
  162. /*
  163. **    Copy the menu array.
  164. **/
  165. STATIC ASM BOOL CopyArray( REG(a0) PMD *pmd, REG(a1) struct PopMenu *pm )
  166. {
  167.     struct PopMenu    *tmp = pm;
  168.     ULONG         size;
  169.     BOOL         rc = FALSE;
  170.  
  171.     /*
  172.     **    Count the number of entries
  173.     **    available in the menu.
  174.     **/
  175.     while ( tmp->pm_Label ) {
  176.         pmd->pmd_NumMenuLabels++;
  177.         tmp++;
  178.     }
  179.  
  180.     /*
  181.     **    Compute the size of the needed
  182.     **    allocation. One extra slot is
  183.     **    allocated to store the terminating
  184.     **    entry.
  185.     **/
  186.     size = ( pmd->pmd_NumMenuLabels + 1 ) * sizeof( struct PopMenu );
  187.  
  188.     /*
  189.     **    Allocate enough memory to hold
  190.     **    the copy of the array.
  191.     **/
  192.     if ( pmd->pmd_MenuLabels = ( struct PopMenu * )AllocVec( size, MEMF_PUBLIC )) {
  193.         /*
  194.         **    Copy the array.
  195.         **/
  196.         CopyMem( pm, pmd->pmd_MenuLabels, size );
  197.         /*
  198.         **    Check if the position is correct.
  199.         **/
  200.         if ( pmd->pmd_PopPos >= pmd->pmd_NumMenuLabels )
  201.             pmd->pmd_PopPos = pmd->pmd_NumMenuLabels - 1;
  202.         /*
  203.         **    Success :)
  204.         **/
  205.         rc = TRUE;
  206.     }
  207.     return( rc );
  208. }
  209.  
  210. /*
  211. **    Scale the checkmark.
  212. **/
  213. STATIC ASM VOID ScaleCheckMark( REG(a0) PMD *pmd, REG(a1) struct RastPort *rp )
  214. {
  215.     UBYTE           *refstr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,_:";
  216.  
  217.     /*
  218.     **    Could probably use some work
  219.     **    but it works for me.
  220.     **/
  221.     pmd->pmd_CheckHeight = rp->TxHeight;
  222.     pmd->pmd_CheckWidth  = TextLength( rp, refstr, 55 ) / 55;
  223.     pmd->pmd_CheckWidth  = pmd->pmd_CheckWidth + 6;
  224.  
  225.     SetAttrs( pmd->pmd_CheckMark, IA_Width, pmd->pmd_CheckWidth, IA_Height, pmd->pmd_CheckHeight, TAG_END );
  226. }
  227.  
  228. /*
  229. **    Re-compute the size of the popup window.
  230. **/
  231. STATIC ASM VOID ComputePopWindowSize( REG(a0) PMD *pmd, REG(a1) struct TextFont *tf )
  232. {
  233.     struct RastPort         rp;
  234.     UWORD            width = 0, height = 0, tlen;
  235.     struct PopMenu           *labels = pmd->pmd_MenuLabels;
  236.  
  237.     /*
  238.     **    Initialize rastport and setup the font.
  239.     **/
  240.     InitRastPort( &rp );
  241.     SetFont( &rp, tf );
  242.  
  243.     ScaleCheckMark( pmd, &rp );
  244.  
  245.     /*
  246.     **    Compute the size of the
  247.     **    window accoording to the
  248.     **    menu labels.
  249.     **/
  250.     while ( labels->pm_Label ) {
  251.         /*
  252.         **    Compute the width of a normal
  253.         **    menu label.
  254.         **/
  255.         if ( labels->pm_Label != PMB_BARLABEL ) {
  256.             if ( labels->pm_Flags & PMF_CHECKIT ) tlen = pmd->pmd_CheckWidth + 2;
  257.             else                      tlen = 0;
  258.             if (( tlen += TextLength( &rp, labels->pm_Label, strlen( labels->pm_Label ))) > width )
  259.                 width = tlen;
  260.             height += tf->tf_YSize + 1;
  261.         } else
  262.             height += 6;
  263.         /*
  264.         **    Next...
  265.         **/
  266.         labels++;
  267.     }
  268.  
  269.     /*
  270.     **    Set values.
  271.     **/
  272.     pmd->pmd_PopWindowWidth  = width  + 12;
  273.     pmd->pmd_PopWindowHeight = height + 4;
  274.  
  275. }
  276.  
  277. /*
  278. **    Render a bar-label.
  279. **/
  280. STATIC ASM VOID RenderBarLabel( REG(a0) struct RastPort *rp, REG(d0) UWORD top, REG(d1) UWORD width )
  281. {
  282.     /*
  283.     **    Render first line.
  284.     **/
  285.     SetDrPt( rp, 0x7777 );        /* %0111011101110111 */
  286.     Move( rp, 6,     top );
  287.     Draw( rp, width, top );
  288.  
  289.     /*
  290.     **    Render second line.
  291.     **/
  292.     SetDrPt( rp, 0xDDDD );        /* %1101110111011101 */
  293.     Move( rp, 6,     top + 1 );
  294.     Draw( rp, width, top + 1 );
  295.  
  296.     SetDrPt( rp, ( UWORD )-1 );    /* %1111111111111111 */
  297. }
  298.  
  299. /*
  300. **    Get the y-position of the item
  301. **    number "entry".
  302. **/
  303. STATIC ASM UWORD GetYPos( REG(a0) struct RastPort *rp, REG(a1) struct PopMenu *labels, REG(d0) ULONG entry )
  304. {
  305.     UWORD            num = 0, ypos = 2;
  306.  
  307.     /*
  308.     **    Pass through the labels updating
  309.     **    the Y-counter until "num" matches "entry".
  310.     **/
  311.     while ( labels->pm_Label && num != entry ) {
  312.         if ( labels->pm_Label == PMB_BARLABEL ) ypos += 6;
  313.         else                    ypos += rp->TxHeight + 1;
  314.         labels++;
  315.         num++;
  316.     }
  317.  
  318.     return( ypos );
  319. }
  320.  
  321. /*
  322. **    Render a popmenu entry.
  323. **/
  324. STATIC VOID RenderMenuEntry( struct RastPort *rp, PMD *pmd, UWORD *pens, ULONG num, BOOL sel, struct DrawInfo *dr )
  325. {
  326.     UWORD            ypos, pena, penb;
  327.     struct PopMenu           *label = &pmd->pmd_MenuLabels[ num ];
  328.  
  329.     /*
  330.     **    Compute entry position.
  331.     **/
  332.     ypos = GetYPos( rp, pmd->pmd_MenuLabels, num );
  333.  
  334.     /*
  335.     **    Setup the correct pens.
  336.     **/
  337.     if ( OS20 ) {
  338.         pena = pens[ DETAILPEN ];
  339.         penb = pens[ BLOCKPEN  ];
  340.     } else {
  341.         pena = pens[ BARDETAILPEN ];
  342.         penb = pens[ BARBLOCKPEN  ];
  343.     }
  344.  
  345.     /*
  346.     **    We only backfill when we render
  347.     **    a normal menu label.
  348.     **/
  349.     if ( label->pm_Label != PMB_BARLABEL ) {
  350.         /*
  351.         **    Backfill the menu label.
  352.         **/
  353.         if ( OS30 ) SetAPen( rp, sel ? pena : penb );
  354.         else        SetAPen( rp, penb );
  355.         SetDrMd( rp, JAM1 );
  356.  
  357.         RectFill( rp, 4, ypos, pmd->pmd_PopWindowWidth - 5, ypos + rp->TxHeight );
  358.     }
  359.  
  360.     /*
  361.     **    Setup drawmode.
  362.     **/
  363.     if ( OS30 ) SetDrMd( rp, sel ? JAM2|INVERSVID : JAM2 );
  364.     else        SetDrMd( rp, JAM2 );
  365.  
  366.     /*
  367.     **    Set the pens.
  368.     **/
  369.     SetAPen( rp, pena );
  370.     SetBPen( rp, penb );
  371.  
  372.     /*
  373.     **    Render the menu label.
  374.     **/
  375.     if ( label->pm_Label != PMB_BARLABEL ) {
  376.         /*
  377.         **    Checkable?
  378.         **/
  379.         if ( label->pm_Flags & PMF_CHECKIT ) {
  380.             /*
  381.             **    Checked?
  382.             **/
  383.             if ( label->pm_Flags & PMF_CHECKED ) {
  384.                 SetAttrs( pmd->pmd_CheckMark, VIT_Pen, sel ? penb : pena, TAG_END );
  385.                 DrawImageState( rp, ( struct Image * )pmd->pmd_CheckMark, 6, ypos, IDS_NORMAL, dr );
  386.             }
  387.             Move( rp, 8 + pmd->pmd_CheckWidth, ypos + rp->TxBaseline + 1 );
  388.         } else
  389.             Move( rp, 6, ypos + rp->TxBaseline + 1 );
  390.         Text( rp, label->pm_Label, strlen( label->pm_Label ));
  391.     } else
  392.         RenderBarLabel( rp, ypos + 2, pmd->pmd_PopWindowWidth - 6 );
  393.  
  394.     /*
  395.     **    Invers the entry if were running
  396.     **    on OS 2.04 and it's selected.
  397.     **/
  398.     if ( OS20 && sel && ( label->pm_Label != PMB_BARLABEL )) {
  399.         SetDrMd( rp, JAM2|COMPLEMENT );
  400.         RectFill( rp, 4, ypos, pmd->pmd_PopWindowWidth - 5, ypos + rp->TxHeight );
  401.     }
  402. }
  403.  
  404. /*
  405. **    Open the popup menu window.
  406. **
  407. **    Please note that if there are more items that
  408. **    there will fit on the screen the list is clipped
  409. **    off at the bottom.
  410. **/
  411. STATIC ASM ULONG OpenPopupWindow( REG(a0) PMD *pmd, REG(a1) Object *obj, REG(a2) struct gpInput *gpi, REG(d0) BOOL mouse )
  412. {
  413.     struct Screen        *screen = gpi->gpi_GInfo->gi_Screen;
  414.     struct RastPort          rpt;
  415.     UWORD             sw, sh, wleft, wtop, wwi, wwh, *pens;
  416.     struct IBox        *ibox;
  417.     struct TextAttr         *tattr = NULL;
  418.     struct TextFont         *tf;
  419.     struct RastPort         *rp;
  420.     ULONG             rc = GMR_NOREUSE;
  421.  
  422.     /*
  423.     **    Get a pointer to the pen array.
  424.     **/
  425.     pens = gpi->gpi_GInfo->gi_DrInfo ? gpi->gpi_GInfo->gi_DrInfo->dri_Pens : DefDriPens;
  426.  
  427.     /*
  428.     **    Get screen dimensions.
  429.     **/
  430.     sw = screen->Width;
  431.     sh = screen->Height;
  432.  
  433.     /*
  434.     **    Get object bounds and the font
  435.     **    we use..
  436.     **/
  437.     DoMethod( obj, OM_GET, BT_HitBox,   &ibox );
  438.     DoMethod( obj, OM_GET, BT_TextAttr, &tattr );
  439.  
  440.     /*
  441.     **    Open the font.
  442.     **/
  443.     if ( tattr ) {
  444.         if ( tf = pmd->pmd_PopFont = OpenFont( tattr )) {
  445.             /*
  446.             **    Compute popwindow size.
  447.             **/
  448.             ComputePopWindowSize( pmd, tf );
  449.  
  450.             /*
  451.             **    Setup dummy rastport.
  452.             **/
  453.             InitRastPort( &rpt );
  454.             SetFont( &rpt, tf );
  455.  
  456.             /*
  457.             **    Copy dimensions.
  458.             **/
  459.             wwi = pmd->pmd_PopWindowWidth;
  460.             wwh = pmd->pmd_PopWindowHeight;
  461.  
  462.             /*
  463.             **    Compute the popwindow
  464.             **    position.
  465.             **/
  466.             if ( mouse ) {
  467.                 /*
  468.                 **    Mouse activation means that the window
  469.                 **    is opened with the first label centered
  470.                 **    under the mouse.
  471.                 **/
  472.                 wleft = screen->MouseX - ( wwi >> 1 );
  473.                 wtop  = screen->MouseY - ( 2 + GetYPos( &rpt, pmd->pmd_MenuLabels, pmd->pmd_PopPos ) + ( tf->tf_YSize >> 1 ));
  474.             } else {
  475.                 /*
  476.                 **    Key actiation will open the window
  477.                 **    under the object.
  478.                 **/
  479.                 wleft  = ibox->Left + (( ibox->Width >> 1 ) - ( wwi >> 1 )) + gpi->gpi_GInfo->gi_Window->LeftEdge;
  480.                 wtop   = ibox->Top + ibox->Height + gpi->gpi_GInfo->gi_Window->TopEdge;
  481.             }
  482.  
  483.  
  484.             /*
  485.             **    Open the pop window.
  486.             **/
  487.             pmd->pmd_PopWindow = OpenWindowTags( NULL, WA_Left,        wleft,
  488.                                    WA_Top,        wtop,
  489.                                    WA_Width,        wwi,
  490.                                    WA_Height,        wwh,
  491.                                    WA_Flags,        0L,
  492.                                    WA_IDCMP,        0L,
  493.                                    WA_Borderless,    TRUE,
  494.                                    WA_AutoAdjust,    TRUE,
  495.                                    WA_SimpleRefresh,    TRUE,
  496.                                    WA_NoCareRefresh,    TRUE,
  497.                                    WA_RMBTrap,        TRUE,
  498.                                    WA_CustomScreen,    screen,
  499.                                    TAG_END );
  500.  
  501.             /*
  502.             **    Success?
  503.             **/
  504.             if ( pmd->pmd_PopWindow ) {
  505.                 /*
  506.                 **    Pick up the rastport.
  507.                 **/
  508.                 rp = pmd->pmd_PopWindow->RPort;
  509.  
  510.                 /*
  511.                 **    Set the font.
  512.                 **/
  513.                 SetFont( rp, tf );
  514.  
  515.                 /*
  516.                 **    Background pen.
  517.                 **/
  518.                 if ( OS20 ) sw = pens[ BLOCKPEN    ];
  519.                 else        sw = pens[ BARBLOCKPEN ];
  520.  
  521.                 /*
  522.                 **    Backfill the menu.
  523.                 **/
  524.                 SetAPen( rp, sw );
  525.                 SetDrMd( rp, JAM1 );
  526.                 RectFill( rp, 0, 0, wwi, wwh );
  527.  
  528.                 /*
  529.                 **    Detail pen.
  530.                 **/
  531.                 if ( OS20 ) sw = pens[ DETAILPEN    ];
  532.                 else        sw = pens[ BARDETAILPEN ];
  533.  
  534.                 /*
  535.                 **    Render frame.
  536.                 **/
  537.                 SetAPen( rp, sw );
  538.  
  539.                 Move( rp, 0, 0 );
  540.                 Draw( rp, wwi - 1, 0);
  541.                 Draw( rp, wwi - 1, wwh - 1 );
  542.                 Draw( rp, 0, wwh - 1 );
  543.                 Draw( rp, 0, 0 );
  544.                 Move( rp, 1, 0 );
  545.                 Draw( rp, 1, wwh - 1 );
  546.                 Move( rp, wwi - 2, 0 );
  547.                 Draw( rp, wwi - 2, wwh - 1 );
  548.  
  549.                 /*
  550.                 **    Render the menus...
  551.                 **/
  552.                 for ( sw = 0; sw < pmd->pmd_NumMenuLabels; sw++ )
  553.                     RenderMenuEntry( rp, pmd, pens, sw, FALSE, gpi->gpi_GInfo->gi_DrInfo );
  554.  
  555.                 rc = GMR_MEACTIVE;
  556.             } else {
  557.                 CloseFont( tf );
  558.                 pmd->pmd_PopFont = NULL;
  559.             }
  560.         }
  561.     }
  562.     return( rc );
  563. }
  564.  
  565. /*
  566. **    Return the menu number under the mouse.
  567. **/
  568. STATIC ASM LONG Selected( REG(a0) PMD *pmd, REG(a1) struct RastPort *rp )
  569. {
  570.     WORD            mx = pmd->pmd_PopWindow->MouseX, my = pmd->pmd_PopWindow->MouseY, ypos = 2;
  571.     struct PopMenu           *labels = pmd->pmd_MenuLabels;
  572.     LONG            item = 0;
  573.  
  574.     /*
  575.     **    Mouse still in the window?
  576.     **/
  577.     if ( mx >= 4 && mx < ( pmd->pmd_PopWindow->Width - 4 ) && my >= 2 && my < ( pmd->pmd_PopWindow->Height - 2 )) {
  578.         /*
  579.         **    Start at Y-offset 2 which is
  580.         **    where the first item is located.
  581.         **/
  582.         ypos = 2;
  583.  
  584.         /*
  585.         **    Go through the labels until the
  586.         **    mouse position is located inside
  587.         **    the offset.
  588.         **/
  589.         while ( labels->pm_Label ) {
  590.             if ( labels->pm_Label == PMB_BARLABEL ) {
  591.                 /*
  592.                 **    Is it on this barlabel?
  593.                 **/
  594.                 if ( my >= ypos && my < ( ypos + 6 ))
  595.                     /*
  596.                     **    Not selectable!
  597.                     **/
  598.                     return( ~0 );
  599.                 ypos += 6;
  600.             } else {
  601.                 /*
  602.                 **    Is it on this item?
  603.                 **/
  604.                 if ( my >= ypos && my < ( ypos + rp->TxHeight + 1 ))
  605.                     /*
  606.                     **    Yes. Return it's number.
  607.                     **/
  608.                     return( item );
  609.                 ypos += rp->TxHeight + 1;
  610.             }
  611.             labels++;
  612.             item++;
  613.         }
  614.     }
  615.     return( ~0 );
  616. }
  617.  
  618. /*
  619. **    Find the previous selectable item.
  620. **/
  621. STATIC ASM ULONG PrevItem( REG(a0) PMD *pmd )
  622. {
  623.     ULONG        prev = pmd->pmd_Selected - 1;
  624.  
  625.     /*
  626.     **    Are we at the top already?
  627.     **/
  628.     if ( ! pmd->pmd_Selected )
  629.         return( pmd->pmd_Selected );
  630.  
  631.     /*
  632.     **    Look up the item.
  633.     **/
  634.     while ( pmd->pmd_MenuLabels[ prev ].pm_Label == PMB_BARLABEL ) {
  635.         if ( ! prev )
  636.             break;
  637.         prev--;
  638.     }
  639.  
  640.     return( pmd->pmd_MenuLabels[ prev ].pm_Label == PMB_BARLABEL ? pmd->pmd_Selected : prev );
  641. }
  642.  
  643. /*
  644. **    Find the next selectable item.
  645. **/
  646. STATIC ASM ULONG NextItem( REG(a0) PMD *pmd )
  647. {
  648.     ULONG        next = pmd->pmd_Selected + 1;
  649.  
  650.     /*
  651.     **    Are we at the end already?
  652.     **/
  653.     if ( pmd->pmd_Selected == pmd->pmd_NumMenuLabels - 1 )
  654.         return( pmd->pmd_Selected );
  655.  
  656.     /*
  657.     **    Look up the item.
  658.     **/
  659.     while ( pmd->pmd_MenuLabels[ next ].pm_Label == PMB_BARLABEL ) {
  660.         if ( next == pmd->pmd_NumMenuLabels - 1 )
  661.             break;
  662.         next++;
  663.     }
  664.  
  665.     return( pmd->pmd_MenuLabels[ next ].pm_Label == PMB_BARLABEL ? pmd->pmd_Selected : next );
  666. }
  667.  
  668. /*
  669. **    Mutually exclude other items.
  670. **/
  671. STATIC ASM VOID MutEx( REG(a0) PMD *pmd )
  672. {
  673.     struct PopMenu           *labels = pmd->pmd_MenuLabels, *the_one = &pmd->pmd_MenuLabels[ pmd->pmd_Selected ];
  674.     UWORD            i;
  675.  
  676.     /*
  677.     **    Anything to exclude?
  678.     **/
  679.     if ( the_one->pm_MutualExclude ) {
  680.         /*
  681.         **    Maximum range which can be
  682.         **    excluded is 32 items.
  683.         **/
  684.         for ( i = 0; i < 32; i++, labels++ ) {
  685.             /*
  686.             **    End of menu?
  687.             **/
  688.             if ( ! labels->pm_Label )
  689.                 break;
  690.             /*
  691.             **    Skip the currently selected
  692.             **    item.
  693.             **/
  694.             if ( labels != the_one ) {
  695.                 /*
  696.                 **    Checkable item?
  697.                 **/
  698.                 if ( labels->pm_Flags & PMF_CHECKIT ) {
  699.                     /*
  700.                     **    Exclude bit set?
  701.                     **/
  702.                     if ( the_one->pm_MutualExclude & ( 1 << i ))
  703.                         /*
  704.                         **    Yes. Un-check it.
  705.                         **/
  706.                         labels->pm_Flags &= ~PMF_CHECKED;
  707.                 }
  708.             }
  709.         }
  710.     }
  711. }
  712.  
  713. /*
  714. **    Compute minimum object dimensions.
  715. **/
  716. STATIC ASM VOID SetDimensions( REG(a0) Object *obj, REG(a1) struct grmDimensions *dim, REG(d0) UWORD mx, REG(d1) UWORD my )
  717. {
  718.     Object                *label = NULL, *frame = NULL;
  719.     ULONG                 place, fh = 0, fv = 0;
  720.  
  721.     /*
  722.     **    Including frame?
  723.     **/
  724.     if ( ! ( dim->grmd_Flags & GDIMF_NO_FRAME )) {
  725.         /*
  726.         **    Obtain frame object.
  727.         **/
  728.         DoMethod( obj, OM_GET, BT_FrameObject, &frame );
  729.         /*
  730.         **    Get width & height.
  731.         **/
  732.         GetAttr( FRM_FrameWidth,  frame, &fh );
  733.         GetAttr( FRM_FrameHeight, frame, &fv );
  734.         fh <<= 1;
  735.         fh  += 4;
  736.         fv <<= 1;
  737.         fv  += 2;
  738.     }
  739.  
  740.     /*
  741.     **    Get object label.
  742.     **/
  743.     DoMethod( obj, OM_GET, BT_LabelObject, &label );
  744.     if ( label ) {
  745.         /*
  746.         **    Get label position.
  747.         **/
  748.         DoMethod( label, OM_GET, LAB_Place, &place );
  749.  
  750.         switch ( place ) {
  751.             case    PLACE_LEFT:
  752.             case    PLACE_IN:
  753.             case    PLACE_RIGHT:
  754.                 if ( my < *( dim->grmd_MinSize.Height )) my  = *( dim->grmd_MinSize.Height );
  755.                 else                     my += fv;
  756.                 break;
  757.             case    PLACE_ABOVE:
  758.             case    PLACE_BELOW:
  759.                 my += *( dim->grmd_MinSize.Height );
  760.                 break;
  761.         }
  762.  
  763.         switch ( place ) {
  764.             case    PLACE_ABOVE:
  765.             case    PLACE_IN:
  766.             case    PLACE_BELOW:
  767.                 if ( mx < *( dim->grmd_MinSize.Width )) mx  = *( dim->grmd_MinSize.Width );
  768.                 else                    mx += fh;
  769.                 break;
  770.             case    PLACE_LEFT:
  771.             case    PLACE_RIGHT:
  772.                 mx += *( dim->grmd_MinSize.Width );
  773.                 break;
  774.         }
  775.     } else {
  776.         mx += fh;
  777.         my += fv;
  778.     }
  779.  
  780.     *( dim->grmd_MinSize.Width  ) = mx;
  781.     *( dim->grmd_MinSize.Height ) = my;
  782. }
  783.  
  784. /*
  785. **    Create a shiny new object.
  786. **/
  787. STATIC ASM ULONG PMBClassNew( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct opSet *ops )
  788. {
  789.     PMD            *pmd;
  790.     struct TagItem        *tstate = ops->ops_AttrList, *tag;
  791.     struct PopMenu        *pm;
  792.     ULONG             rc = 0L;
  793.  
  794.     /*
  795.     **    First we let the superclass
  796.     **    create an object.
  797.     **/
  798.     if ( rc = DoSuperMethodA( cl, obj, ( Msg )ops )) {
  799.         /*
  800.         **    Get to the instance data.
  801.         **/
  802.         pmd = ( PMD * )INST_DATA( cl, rc );
  803.  
  804.         /*
  805.         **    Is this necessary?
  806.         **/
  807.         bzero(( char * )pmd, sizeof( PMD ));
  808.  
  809.         /*
  810.         **    Get attributes.
  811.         **/
  812.         while ( tag = NextTagItem( &tstate )) {
  813.             switch ( tag->ti_Tag ) {
  814.  
  815.                 case    PMB_Image:
  816.                     pmd->pmd_Image = ( struct Image * )tag->ti_Data;
  817.                     break;
  818.  
  819.                 case    PMB_MenuEntries:
  820.                     pm = ( struct PopMenu * )tag->ti_Data;
  821.                     break;
  822.  
  823.                 case    PMB_PopPosition:
  824.                     pmd->pmd_PopPos = tag->ti_Data;
  825.                     break;
  826.             }
  827.         }
  828.  
  829.         /*
  830.         **    If we do not have a intuition image
  831.         **    we go for a vectorclass image.
  832.         **/
  833.         if ( ! pmd->pmd_Image )
  834.             pmd->pmd_VectorImage = CreateVectorImage( ops->ops_AttrList );
  835.  
  836.         /*
  837.         **    We _must_ have menu entries.
  838.         **/
  839.         if ( pm && CopyArray( pmd, pm )) {
  840.             /*
  841.             **    Force the correct activation flags.
  842.             **/
  843.             SetAttrs(( Object * )rc, GA_Immediate, FALSE, GA_RelVerify, TRUE, TAG_END );
  844.             if ( pmd->pmd_CheckMark = BGUI_NewObject( BGUI_VECTOR_IMAGE, VIT_VectorArray, myCheckMark, IA_Left, 0, IA_Top, 0, TAG_END ))
  845.                 return( rc );
  846.         }
  847.  
  848.         /*
  849.         **    Otherwise we fail :(
  850.         **/
  851.         CoerceMethod( cl, ( Object * )rc, OM_DISPOSE );
  852.     }
  853.     return( NULL );
  854. }
  855.  
  856. /*
  857. **    Set object attributes.
  858. **/
  859. STATIC ASM ULONG PMBClassSet( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct opSet *ops )
  860. {
  861.     PMD            *pmd = ( PMD * )INST_DATA( cl, obj );
  862.     struct TagItem        *tstate = ops->ops_AttrList, *tag;
  863.     struct RastPort         *rp;
  864.     ULONG             rc = 0L;
  865.     WORD             dis = GADGET( obj )->Flags & GFLG_DISABLED;
  866.     BOOL             update = FALSE;
  867.  
  868.     /*
  869.     **    Be curtious, let the superclass go
  870.     **    first.
  871.     **/
  872.     rc = DoSuperMethodA( cl, obj, ( Msg )ops );
  873.  
  874.     /*
  875.     **    Get tags.
  876.     **/
  877.     while ( tag = NextTagItem( &tstate )) {
  878.         switch ( tag->ti_Tag ) {
  879.  
  880.             case    VIT_VectorArray:
  881.             case    VIT_BuiltIn:
  882.             case    VIT_Pen:
  883.             case    VIT_DriPen:
  884.                 /*
  885.                 **    Only set when a vector image
  886.                 **    exists!
  887.                 **/
  888.                 if ( pmd->pmd_VectorImage ) {
  889.                     SetAttrs( pmd->pmd_VectorImage, tag->ti_Tag, tag->ti_Data, TAG_END );
  890.                     update = TRUE;
  891.                 }
  892.                 break;
  893.  
  894.             case    PMB_Image:
  895.                 /*
  896.                 **    Only changeable when a previous image
  897.                 **    exists!
  898.                 **
  899.                 **    Note that you are responsible of making
  900.                 **    sure the image fit's inside the dimensions
  901.                 **    of the one you're replacing!
  902.                 **/
  903.                 if ( pmd->pmd_Image ) {
  904.                     pmd->pmd_Image = ( struct Image * )tag->ti_Data;
  905.                     update = TRUE;
  906.                 }
  907.                 break;
  908.         }
  909.     }
  910.  
  911.     /*
  912.     **    Visual update necessary?
  913.     **/
  914.     if ((( GADGET( obj )->Flags & GFLG_DISABLED ) != dis ) || update ) {
  915.         /*
  916.         **    Re-render the object.
  917.         **/
  918.         if ( ops->ops_GInfo ) {
  919.             if ( rp = ObtainGIRPort( ops->ops_GInfo )) {
  920.                 DoMethod( obj, GM_RENDER, ops->ops_GInfo, rp, GREDRAW_REDRAW );
  921.                 ReleaseGIRPort( rp );
  922.             }
  923.         }
  924.     }
  925.     return( rc );
  926. }
  927.  
  928. /*
  929. **    Re-render the object.
  930. **/
  931. STATIC ASM ULONG PMBClassRender( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct gpRender *gpr )
  932. {
  933.     PMD            *pmd = ( PMD * )INST_DATA( cl, obj );
  934.     struct RastPort         *rp = gpr->gpr_RPort;
  935.     struct IBox        *ibox;
  936.     static UWORD         ghostpat[] = { 0x2222, 0x8888 };
  937.     UWORD            *pens, xoff, yoff;
  938.     ULONG             rc;
  939.  
  940.     /*
  941.     **    First the superclass. If that returns
  942.     **    NULL we don't render.
  943.     **/
  944.     if ( rc = DoSuperMethodA( cl, obj, ( Msg )gpr )) {
  945.         /*
  946.         **    Get hitbox bounds.
  947.         **/
  948.         DoMethod( obj, OM_GET, BT_HitBox, &ibox );
  949.  
  950.         /*
  951.         **    Image to render?
  952.         **/
  953.         if ( pmd->pmd_Image ) {
  954.             /*
  955.             **    Set the left and top edge.
  956.             **/
  957.             xoff = ( ibox->Width  >> 1 ) - ( pmd->pmd_Image->Width    >> 1 ) + ibox->Left;
  958.             yoff = ( ibox->Height >> 1 ) - ( pmd->pmd_Image->Height >> 1 ) + ibox->Top;
  959.             /*
  960.             **    Render it.
  961.             **/
  962.             DrawImageState( rp, pmd->pmd_Image, xoff, yoff, IDS_NORMAL, gpr->gpr_GInfo->gi_DrInfo );
  963.         } else if ( pmd->pmd_VectorImage ) {
  964.             /*
  965.             **    Set bounds.
  966.             **/
  967.             SetAttrs( pmd->pmd_VectorImage, IA_Left,   ibox->Left,
  968.                             IA_Top,    ibox->Top,
  969.                             IA_Width,  ibox->Width,
  970.                             IA_Height, ibox->Height,
  971.                             TAG_END );
  972.             /*
  973.             **    Render it.
  974.             **/
  975.             DrawImageState( rp, ( struct Image * )pmd->pmd_VectorImage, 0, 0, IDS_NORMAL, gpr->gpr_GInfo->gi_DrInfo );
  976.         }
  977.         /*
  978.         **    Object disabled?
  979.         **/
  980.         if ( GADGET( obj )->Flags & GFLG_DISABLED ) {
  981.             /*
  982.             **    Get the hitbox dimensions.
  983.             **/
  984.             DoMethod( obj, OM_GET, BT_HitBox, &ibox );
  985.             /*
  986.             **    Pick up pen array.
  987.             **/
  988.             pens = gpr->gpr_GInfo->gi_DrInfo ? gpr->gpr_GInfo->gi_DrInfo->dri_Pens : DefDriPens;
  989.             /*
  990.             **    Ghost it.
  991.             **/
  992.             SetAfPt( gpr->gpr_RPort, ghostpat, 1 );
  993.             SetAPen( gpr->gpr_RPort, pens[ SHADOWPEN ] );
  994.             SetDrMd( gpr->gpr_RPort, JAM1 );
  995.             RectFill( gpr->gpr_RPort, ibox->Left, ibox->Top, ibox->Left + ibox->Width - 1, ibox->Top + ibox->Height - 1 );
  996.         }
  997.     }
  998.     return( rc );
  999. }
  1000.  
  1001. /*
  1002. **    Let's go active.
  1003. **/
  1004. STATIC ASM ULONG PMBClassGoActive( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct gpInput *gpi )
  1005. {
  1006.     PMD            *pmd = ( PMD * )INST_DATA( cl, obj );
  1007.     UWORD            *pens;
  1008.     ULONG             rc = GMR_NOREUSE;
  1009.  
  1010.     /*
  1011.     **    Disabled means that we refuse to go active.
  1012.     **/
  1013.     if ( GADGET( obj )->Flags & GFLG_DISABLED )
  1014.         return( rc );
  1015.  
  1016.     /*
  1017.     **    By default there's nothing selected.
  1018.     **/
  1019.     pmd->pmd_Selected = ~0;
  1020.  
  1021.     /*
  1022.     **    Let's open the popup window.
  1023.     **/
  1024.     if (( rc = OpenPopupWindow( pmd, obj, gpi, gpi->gpi_IEvent ? TRUE : FALSE )) == GMR_MEACTIVE ) {
  1025.         /*
  1026.         **    Activated by ActivateGadget()?
  1027.         **/
  1028.         if ( ! gpi->gpi_IEvent ) {
  1029.             /*
  1030.             **    Mark us as key-activated.
  1031.             **/
  1032.             pmd->pmd_Flags |= PMDF_NOEVENT;
  1033.             /*
  1034.             **    Pick up pen array.
  1035.             **/
  1036.             pens = gpi->gpi_GInfo->gi_DrInfo ? gpi->gpi_GInfo->gi_DrInfo->dri_Pens : DefDriPens;
  1037.             /*
  1038.             **    Pre-select the first menu.
  1039.             **/
  1040.             pmd->pmd_Selected = pmd->pmd_PopPos;
  1041.             /*
  1042.             **    Make sure we get a selectable item.
  1043.             **    This will definitly break when there are
  1044.             **    no selectable items :(
  1045.             **/
  1046.             while ( pmd->pmd_MenuLabels[ pmd->pmd_Selected ].pm_Label == PMB_BARLABEL )
  1047.                 pmd->pmd_Selected++;
  1048.             /*
  1049.             **    Select the item.
  1050.             **/
  1051.             RenderMenuEntry( pmd->pmd_PopWindow->RPort, pmd, pens, pmd->pmd_Selected, TRUE, gpi->gpi_GInfo->gi_DrInfo );
  1052.         }
  1053.     }
  1054.     return( rc );
  1055. }
  1056.  
  1057. /*
  1058. **    Handle the user input.
  1059. **/
  1060. STATIC ASM ULONG PMBClassHandleInput( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct gpInput *gpi )
  1061. {
  1062.     PMD            *pmd = ( PMD * )INST_DATA( cl, obj );
  1063.     ULONG             item, rc = GMR_MEACTIVE;
  1064.     UWORD            *pens;
  1065.  
  1066.     /*
  1067.     **    The keyboard control of the menu does not
  1068.     **    work correctly when you have an sunmouse/key
  1069.     **    like tool like Yak running which automatically
  1070.     **    activates the window under the mouse.
  1071.     **
  1072.     **    Intuition will automatically deactivate the
  1073.     **    object when the parent window becomes deactivated.
  1074.     **
  1075.     **    The below code is just to make sure.
  1076.     **/
  1077.     if ( ! ( gpi->gpi_GInfo->gi_Window->Flags & WFLG_WINDOWACTIVE ))
  1078.         return( GMR_NOREUSE );
  1079.  
  1080.     /*
  1081.     **    Pick up pen array.
  1082.     **/
  1083.     pens = gpi->gpi_GInfo->gi_DrInfo ? gpi->gpi_GInfo->gi_DrInfo->dri_Pens : DefDriPens;
  1084.  
  1085.     /*
  1086.     **    Mouse activated?
  1087.     **/
  1088.     if ( ! ( pmd->pmd_Flags & PMDF_NOEVENT )) {
  1089.         /*
  1090.         **    Where's the mouse.
  1091.         **/
  1092.         item = Selected( pmd, pmd->pmd_PopWindow->RPort );
  1093.         /*
  1094.         **    Did it change?
  1095.         **/
  1096.         if ( item != pmd->pmd_Selected ) {
  1097.             /*
  1098.             **    Unselect current entry and
  1099.             **    select the new one.
  1100.             **/
  1101.             if ( pmd->pmd_Selected != ~0 ) RenderMenuEntry( pmd->pmd_PopWindow->RPort, pmd, pens, pmd->pmd_Selected, FALSE, gpi->gpi_GInfo->gi_DrInfo );
  1102.             if ( item != ~0 )           RenderMenuEntry( pmd->pmd_PopWindow->RPort, pmd, pens, item, TRUE, gpi->gpi_GInfo->gi_DrInfo );
  1103.             /*
  1104.             **    Set it up.
  1105.             **/
  1106.             pmd->pmd_Selected = item;
  1107.         }
  1108.         /*
  1109.         **    Let's see the mouse stuff...
  1110.         **/
  1111.         if ( gpi->gpi_IEvent->ie_Class == IECLASS_RAWMOUSE ) {
  1112.             switch ( gpi->gpi_IEvent->ie_Code ) {
  1113.  
  1114.                 case    SELECTUP:
  1115.                     /*
  1116.                     **    Report a change to the application.
  1117.                     **/
  1118.                     if ( pmd->pmd_Selected != ~0 ) {
  1119.                         /*
  1120.                         **    Notify the selection.
  1121.                         **/
  1122.                         NotifyAttrChange( obj, gpi->gpi_GInfo, 0L, GA_ID, GADGET( obj )->GadgetID, PMB_MenuNumber, pmd->pmd_Selected, TAG_END );
  1123.                         /*
  1124.                         **    PMF_CHECKIT item?
  1125.                         **/
  1126.                         if ( pmd->pmd_MenuLabels[ pmd->pmd_Selected ].pm_Flags & PMF_CHECKIT ) {
  1127.                             /*
  1128.                             **    Toggle PMF_CHECKED bit.
  1129.                             **/
  1130.                             pmd->pmd_MenuLabels[ pmd->pmd_Selected ].pm_Flags ^= PMF_CHECKED;
  1131.                             /*
  1132.                             **    Do mutual exclusion.
  1133.                             **/
  1134.                             MutEx( pmd );
  1135.                         }
  1136.                         /*
  1137.                         **    Return GMR_VERIFY. Tell intuition to
  1138.                         **    discard the input event.
  1139.                         **/
  1140.                         rc = GMR_NOREUSE | GMR_VERIFY;
  1141.                     } else
  1142.                         rc = GMR_NOREUSE;
  1143.                     break;
  1144.  
  1145.                 case    MENUDOWN:
  1146.                     /*
  1147.                     **    Abort the mission and let
  1148.                     **    intuition snap down the
  1149.                     **    menus.
  1150.                     **/
  1151.                     rc = GMR_REUSE;
  1152.                     break;
  1153.             }
  1154.         }
  1155.     } else {
  1156.         if ( gpi->gpi_IEvent->ie_Class == IECLASS_RAWKEY ) {
  1157.             switch ( gpi->gpi_IEvent->ie_Code ) {
  1158.  
  1159.                 case    0x4C:
  1160.                     /*
  1161.                     **    CURSOR UP
  1162.                     **/
  1163.                     if (( item =  PrevItem( pmd )) != pmd->pmd_Selected ) {
  1164.                         RenderMenuEntry( pmd->pmd_PopWindow->RPort, pmd, pens, pmd->pmd_Selected, FALSE, gpi->gpi_GInfo->gi_DrInfo );
  1165.                         pmd->pmd_Selected = item;
  1166.                         RenderMenuEntry( pmd->pmd_PopWindow->RPort, pmd, pens, pmd->pmd_Selected, TRUE, gpi->gpi_GInfo->gi_DrInfo );
  1167.                     }
  1168.                     break;
  1169.  
  1170.                 case    0x4D:
  1171.                     /*
  1172.                     **    CURSOR DOWN
  1173.                     **/
  1174.                     if (( item = NextItem( pmd )) != pmd->pmd_Selected ) {
  1175.                         RenderMenuEntry( pmd->pmd_PopWindow->RPort, pmd, pens, pmd->pmd_Selected, FALSE, gpi->gpi_GInfo->gi_DrInfo );
  1176.                         pmd->pmd_Selected = item;
  1177.                         RenderMenuEntry( pmd->pmd_PopWindow->RPort, pmd, pens, pmd->pmd_Selected, TRUE, gpi->gpi_GInfo->gi_DrInfo );
  1178.                     }
  1179.                     break;
  1180.  
  1181.                 case    0x44:
  1182.                     /*
  1183.                     **    RETURN
  1184.                     **/
  1185.                 case    0x43:
  1186.                     /*
  1187.                     **    ENTER
  1188.                     **/
  1189.                     NotifyAttrChange( obj, gpi->gpi_GInfo, 0L, GA_ID, GADGET( obj )->GadgetID, PMB_MenuNumber, pmd->pmd_Selected, TAG_END );
  1190.                     /*
  1191.                     **    PMF_CHECKIT item?
  1192.                     **/
  1193.                     if ( pmd->pmd_MenuLabels[ pmd->pmd_Selected ].pm_Flags & PMF_CHECKIT ) {
  1194.                         /*
  1195.                         **    Toggle PMF_CHECKED bit.
  1196.                         **/
  1197.                         pmd->pmd_MenuLabels[ pmd->pmd_Selected ].pm_Flags ^= PMF_CHECKED;
  1198.                         /*
  1199.                         **    Do mutual exclusion.
  1200.                         **/
  1201.                         MutEx( pmd );
  1202.                     }
  1203.                     /*
  1204.                     **    Return GMR_VERIFY. Tell intuition to
  1205.                     **    discard the input event.
  1206.                     **/
  1207.                     rc = GMR_NOREUSE | GMR_VERIFY;
  1208.                     break;
  1209.  
  1210.                 case    0x45:
  1211.                     /*
  1212.                     **    ESC
  1213.                     **/
  1214.                     rc = GMR_NOREUSE;
  1215.                     break;
  1216.             }
  1217.         }
  1218.     }
  1219.     return( rc );
  1220. }
  1221.  
  1222. /*
  1223. **    Go inactive...
  1224. **/
  1225. STATIC ASM ULONG PMBClassGoInActive( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct gpGoInactive *gpgi )
  1226. {
  1227.     PMD            *pmd = ( PMD * )INST_DATA( cl, obj );
  1228.  
  1229.     /*
  1230.     **    Close up shop.
  1231.     **/
  1232.     if ( pmd->pmd_PopWindow ) {
  1233.         CloseWindow( pmd->pmd_PopWindow );
  1234.         pmd->pmd_PopWindow = NULL;
  1235.     }
  1236.  
  1237.     if ( pmd->pmd_PopFont ) {
  1238.         CloseFont( pmd->pmd_PopFont );
  1239.         pmd->pmd_PopFont   = NULL;
  1240.     }
  1241.  
  1242.     /*
  1243.     **    Clear ActivateGadget() flag.
  1244.     **/
  1245.     pmd->pmd_Flags &= ~PMDF_NOEVENT;
  1246.  
  1247.     return( DoSuperMethodA( cl, obj, ( Msg )gpgi ));
  1248. }
  1249.  
  1250. /*
  1251. **    Get an attribute.
  1252. **/
  1253. STATIC ASM ULONG PMBClassGet( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct opGet *opg )
  1254. {
  1255.     PMD            *pmd = ( PMD * )INST_DATA( cl, obj );
  1256.     ULONG             rc;
  1257.  
  1258.     /*
  1259.     **    We only know one attribute.
  1260.     **/
  1261.     if ( opg->opg_AttrID == PMB_MenuNumber ) {
  1262.         *( opg->opg_Storage ) = pmd->pmd_Selected;
  1263.         rc = 1L;
  1264.     } else
  1265.         rc = DoSuperMethodA( cl, obj, ( Msg )opg );
  1266.  
  1267.     return( rc );
  1268. }
  1269.  
  1270. /*
  1271. **    Dump the object.
  1272. **/
  1273. STATIC ASM ULONG PMBClassDispose( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) Msg msg )
  1274. {
  1275.     PMD            *pmd = ( PMD * )INST_DATA( cl, obj );
  1276.  
  1277.     /*
  1278.     **    If we have a vector image
  1279.     **    we dispose of it here.
  1280.     **/
  1281.     if ( pmd->pmd_VectorImage ) DisposeObject( pmd->pmd_VectorImage );
  1282.     if ( pmd->pmd_CheckMark   ) DisposeObject( pmd->pmd_CheckMark    );
  1283.  
  1284.     /*
  1285.     **    Free the allocated PopMenu
  1286.     **    structure array.
  1287.     **/
  1288.     if ( pmd->pmd_MenuLabels  ) FreeVec( pmd->pmd_MenuLabels );
  1289.  
  1290.     /*
  1291.     **    The rest is for the superclass.
  1292.     **/
  1293.     return( DoSuperMethodA( cl, obj, msg ));
  1294. }
  1295.  
  1296. /*
  1297.  *    They want our minimum dimensions.
  1298.  */
  1299. STATIC ASM ULONG PMBClassDimensions( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct grmDimensions *dim )
  1300. {
  1301.     PMD            *pmd = ( PMD * )INST_DATA( cl, obj );
  1302.     UWORD             mx, my;
  1303.     ULONG             frame, fw, fh;
  1304.     struct grmDimensions     dim1 = *dim;
  1305.     struct VectorItem    *vit;
  1306.  
  1307.     if ( pmd->pmd_Image ) {
  1308.         /*
  1309.         **    Get minimum size of
  1310.         **    the image.
  1311.         **/
  1312.         mx = pmd->pmd_Image->Width;
  1313.         my = pmd->pmd_Image->Height;
  1314.  
  1315.         /*
  1316.         **    We enclose the image in the
  1317.         **    frame which means that we do
  1318.         **    the frame computation ourselves.
  1319.         **/
  1320.         dim1.grmd_Flags |= GDIMF_NO_FRAME;
  1321.  
  1322.         /*
  1323.         **    Let the superclass have a go at it.
  1324.         **/
  1325.         DoSuperMethodA( cl, obj, ( Msg )&dim1 );
  1326.  
  1327.         /*
  1328.         **    Add frame thickness.
  1329.         **/
  1330.         DoMethod( obj, OM_GET, BT_FrameObject, &frame );
  1331.         if ( frame ) {
  1332.             DoMethod(( Object * )frame, OM_GET, FRM_FrameWidth,  &fw );
  1333.             DoMethod(( Object * )frame, OM_GET, FRM_FrameHeight, &fh );
  1334.             mx += fw << 1;
  1335.             my += fh << 1;
  1336.         }
  1337.  
  1338.         /*
  1339.          *    Set it.
  1340.          */
  1341.         SetDimensions( obj, &dim1, mx, my );
  1342.         return( 1L );
  1343.     } else if ( pmd->pmd_VectorImage ) {
  1344.         /*
  1345.         **    Get minimum vector sizes.
  1346.         **/
  1347.         DoMethod( pmd->pmd_VectorImage, OM_GET, VIT_VectorArray, ( ULONG * )&vit );
  1348.  
  1349.         /*
  1350.         **    Scan for the sizes.
  1351.         **/
  1352.         while( 1 ) {
  1353.             /*
  1354.             **    The scaling size is considered
  1355.             **    to be the minimum object size.
  1356.             **/
  1357.             if ( vit->vi_Flags & VIF_SCALE ) {
  1358.                 mx = vit->vi_x;
  1359.                 my = vit->vi_y;
  1360.                 break;
  1361.             }
  1362.  
  1363.             /*
  1364.             **    Last one?
  1365.             **/
  1366.             if ( vit->vi_Flags & VIF_LASTITEM )
  1367.                 goto doSuper;
  1368.  
  1369.             /*
  1370.             **    Next please...
  1371.             **/
  1372.             vit++;
  1373.         }
  1374.  
  1375.         /*
  1376.         **    First the superclass.
  1377.         **/
  1378.         dim1.grmd_Flags |= GDIMF_NO_FRAME;
  1379.         DoSuperMethodA( cl, obj, ( Msg )&dim1 );
  1380.  
  1381.         /*
  1382.         **    Now add our minimum sizes.
  1383.         **/
  1384.         SetDimensions( obj, &dim1, mx, my );
  1385.         return( 1L );
  1386.     }
  1387.  
  1388.     doSuper:
  1389.     /*
  1390.     **    Normal button.
  1391.     **/
  1392.     return( DoSuperMethodA( cl, obj, ( Msg )dim ));
  1393. }
  1394.  
  1395. /*
  1396. **    Do a command on the menu.
  1397. **/
  1398. STATIC ASM ULONG PMBClassCommand( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct pmbmCommand *pc )
  1399. {
  1400.     PMD            *pmd = ( PMD * )INST_DATA( cl, obj );
  1401.     struct PopMenu        *label = &pmd->pmd_MenuLabels[ pc->pmbm_MenuNumber ];
  1402.     ULONG             rc = TRUE;
  1403.  
  1404.     /*
  1405.     **    What do they want?
  1406.     **/
  1407.     switch ( pc->MethodID ) {
  1408.  
  1409.         case    PMBM_CHECK_STATUS:
  1410.             /*
  1411.             **    TRUE means checked and FALSE means
  1412.             **    not checked.
  1413.             **/
  1414.             rc = label->pm_Flags & PMF_CHECKED ? TRUE : FALSE;
  1415.             break;
  1416.  
  1417.         case    PMBM_CHECK_MENU:
  1418.             /*
  1419.             **    Check the menu and do
  1420.             **    mutual-exclusion.
  1421.             **/
  1422.             label->pm_Flags |= PMF_CHECKED;
  1423.             MutEx( pmd );
  1424.             break;
  1425.  
  1426.         case    PMBM_UNCHECK_MENU:
  1427.             /*
  1428.             **    Uncheck the menu.
  1429.             **/
  1430.             label->pm_Flags &= ~PMF_CHECKED;
  1431.             break;
  1432.     }
  1433.  
  1434.     return( rc );
  1435. }
  1436.  
  1437. /*
  1438. **    The dispatcher.
  1439. **
  1440. **    SAS Users: You should either compile this module with
  1441. **           stack checking turned off (NOSTACKCHECK) or
  1442. **           you must use the "__interrupt" qualifier in
  1443. **           this routine.
  1444. **/
  1445. STATIC SAVEDS ASM ULONG PMBClassDispatch( REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) Msg msg )
  1446. {
  1447.     ULONG            rc;
  1448.  
  1449.     /*
  1450.     **    What do we have here...
  1451.     **/
  1452.     switch ( msg->MethodID ) {
  1453.  
  1454.         case    OM_NEW:
  1455.             rc = PMBClassNew( cl, obj, ( struct opSet * )msg );
  1456.             break;
  1457.  
  1458.         case    OM_SET:
  1459.             rc = PMBClassSet( cl, obj, ( struct opSet * )msg );
  1460.             break;
  1461.  
  1462.         case    OM_GET:
  1463.             rc = PMBClassGet( cl, obj, ( struct opGet * )msg );
  1464.             break;
  1465.  
  1466.         case    OM_DISPOSE:
  1467.             rc = PMBClassDispose( cl, obj, msg );
  1468.             break;
  1469.  
  1470.         case    GM_RENDER:
  1471.             rc = PMBClassRender( cl, obj, ( struct gpRender * )msg );
  1472.             break;
  1473.  
  1474.         case    GM_GOACTIVE:
  1475.             rc = PMBClassGoActive( cl, obj, ( struct gpInput * )msg );
  1476.             break;
  1477.  
  1478.         case    GM_HANDLEINPUT:
  1479.             rc = PMBClassHandleInput( cl, obj, ( struct gpInput * )msg );
  1480.             break;
  1481.  
  1482.         case    GM_GOINACTIVE:
  1483.             rc = PMBClassGoInActive( cl, obj, ( struct gpGoInactive * )msg );
  1484.             break;
  1485.  
  1486.         case    GRM_DIMENSIONS:
  1487.             rc = PMBClassDimensions( cl, obj, ( struct grmDimensions * )msg );
  1488.             break;
  1489.  
  1490.         case    WM_KEYACTIVE:
  1491.             rc = WMKF_ACTIVATE;
  1492.             break;
  1493.  
  1494.         case    PMBM_CHECK_STATUS:
  1495.         case    PMBM_CHECK_MENU:
  1496.         case    PMBM_UNCHECK_MENU:
  1497.             rc = PMBClassCommand( cl, obj, ( struct pmbmCommand * )msg );
  1498.             break;
  1499.  
  1500.         default:
  1501.             rc = DoSuperMethodA( cl, obj, msg );
  1502.             break;
  1503.     }
  1504.     return( rc );
  1505. }
  1506.  
  1507. /*
  1508. **    Initialize the class.
  1509. **/
  1510. Class *InitPMBClass( void )
  1511. {
  1512.     Class            *super, *cl = NULL;
  1513.  
  1514.     /*
  1515.     **    Obtain the BaseClass pointer which
  1516.     **    will be our superclass.
  1517.     **/
  1518.     if ( super = BGUI_GetClassPtr( BGUI_BASE_GADGET )) {
  1519.         /*
  1520.         **    Create the class.
  1521.         **/
  1522.         if ( cl = MakeClass( NULL, NULL, super, sizeof( PMD ), 0L ))
  1523.             /*
  1524.             **    Setup dispatcher.
  1525.             **/
  1526.             cl->cl_Dispatcher.h_Entry = ( HOOKFUNC )PMBClassDispatch;
  1527.     }
  1528.     return( cl );
  1529. }
  1530.  
  1531. /*
  1532. **    Dispose of the class.
  1533. **/
  1534. BOOL FreePMBClass( Class *cl )
  1535. {
  1536.     return( FreeClass( cl ));
  1537. }
  1538.  
  1539. /************ Simple, eh? ************/
  1540.